package com.liruan.rest.aop;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.liruan.rest.controller.response.JSONResponse;
import com.liruan.rest.dao.UserInfoMapper;
import com.liruan.rest.dto.AuthTokenDTO;
import com.liruan.rest.dto.DTOFactory;
import com.liruan.rest.dto.LogginDTO;
import com.liruan.rest.dto.ResultCodeOnlyDTO;
import com.liruan.rest.dto.UserRegisterDTO;
import com.liruan.rest.exception.BusinessException;
import com.liruan.rest.po.POFactory;
import com.liruan.rest.po.UserInfoPO;
import com.liruan.rest.service.UserService;
import com.liruan.rest.service.result.ResultCode;
import com.liruan.rest.util.MD5Util;

/**
 *用以实现权限系统的AOP拦截器
 *
 */
@Aspect
@Service
public class AuthInterceptor implements UserService
{
	private static final Logger LOGGER = LoggerFactory.getLogger(AuthInterceptor.class);

	private static final String TOKEN_HEADER_NAME = "auth-token";

	private static final String TOKEN_KEY = "CPY7LM5CcScFeM9M1HWb";

	private static final String PASSWORD_SALT = "Vfqbg6Ad23AD9SkAcjL";

	private static final int PASS_WORD_LENGTH_MIN = 6;

	private static final int PASS_WORD_LENGTH_MAX = 20;

	private static final Pattern PASS_WORD_PATTERN = Pattern.compile("[0-9a-zA-Z]+");

	private static final Pattern PHONE_PATTERN = Pattern.compile("\\d{11}");

	private static final String JWT_CLAIM_TIME_STAMP = "auth-timestamp";

	private static final String JWT_CLAIM_USER_ID = "user-id";
	
	private static final String USER_ID_ATTR_KEY = "userId";

	private static final Map<String, Object> JWT_HEADER_CLAIMS = new HashMap<String, Object>();
	static
	{
		JWT_HEADER_CLAIMS.put("typ", "JWT");
		JWT_HEADER_CLAIMS.put("alg", "HS256");
	}

	@Autowired
	private UserInfoMapper userInfoMapper;

	@Pointcut("execution(* com.liruan.rest.controller..*(..)) && @annotation(com.liruan.rest.annotation.AuthRequired)")
	public void controllerMethodPointcut()
	{
	}

	public Long getUserId()
	{
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		return (Long)request.getAttribute(USER_ID_ATTR_KEY);
	}

	private boolean verify(String token)
	{
		try
		{
			JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_KEY)).build();
			DecodedJWT decodedJWT = verifier.verify(token);

			LOGGER.info("验证token:{} 发布时间:{} 用户ID:{}", token, decodedJWT.getClaim(JWT_CLAIM_TIME_STAMP).asLong(), decodedJWT.getClaim(JWT_CLAIM_USER_ID).asLong());
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
			request.setAttribute(USER_ID_ATTR_KEY, decodedJWT.getClaim(JWT_CLAIM_USER_ID).asLong());
		}
		catch (Exception e)
		{
			LOGGER.info("token({})鉴定失败!", token, e);
			return false;
		}

		return true;
	}

	private String doAuth(Long userId)
	{

		try
		{
			return JWT.create().withHeader(JWT_HEADER_CLAIMS).withClaim(JWT_CLAIM_USER_ID, userId).withClaim(JWT_CLAIM_TIME_STAMP, System.currentTimeMillis()).sign(Algorithm.HMAC256(TOKEN_KEY));
		}
		catch (Exception e)
		{
			LOGGER.info("impossible ERROR happen creating jwt", e);
			throw new BusinessException(e, ResultCode.AUTH_FAIL);
		}
	}

	@Around("controllerMethodPointcut()")
	public Object auth(ProceedingJoinPoint pjp) throws Throwable
	{
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
		String token = request.getHeader(TOKEN_HEADER_NAME);
		if (token == null)
		{
			LOGGER.info("IP:{}, 未授权的操作:{}", request.getRemoteAddr(), pjp.getSignature().getName());

			ResultCodeOnlyDTO res = DTOFactory.createResultCodeOnlyDTO(ResultCode.EXCESS_AUTH);
			return new JSONResponse<ResultCodeOnlyDTO>(res.getResultCode(), res);
		}

		if (!verify(token))
		{
			ResultCodeOnlyDTO res = DTOFactory.createResultCodeOnlyDTO(ResultCode.EXCESS_AUTH);
			return new JSONResponse<ResultCodeOnlyDTO>(res.getResultCode(), res);
		}
		return pjp.proceed();
	}

	@Override
	public AuthTokenDTO loggin(LogginDTO dto)
	{
		if (dto == null || dto.getPassword() == null || dto.getUserPhone() == null)
		{
			return DTOFactory.createAuthTokenDTO(null, null, ResultCode.NULL_PARAM);
		}

		try
		{
			String phone = dto.getUserPhone();
			String password = dto.getPassword();

			String passwordMD5 = MD5Util.MD5(password + PASSWORD_SALT);

			UserInfoPO user = userInfoMapper.selectUser(phone, passwordMD5);
			if (user == null)
			{
				LOGGER.info("手机:{} 或 密码:{} 错误 MD5:{}", phone, password, passwordMD5);
				return DTOFactory.createAuthTokenDTO(null, null, ResultCode.NO_SUCH_USER);
			}

			String token = doAuth(user.getId());
			LOGGER.info("为手机:{} 或 密码:{} 生成token:{} ", phone, password, token);
			return DTOFactory.createAuthTokenDTO(token, user.getId(), ResultCode.GENERAL_SUCCESS);

		}
		catch (BusinessException e)
		{
			LOGGER.info(e.getResultCode().getDesc(), e);
			return DTOFactory.createAuthTokenDTO(null, null, e.getResultCode());
		}
		catch (Exception e)
		{
			LOGGER.info("授权时发生异常", e);
			return DTOFactory.createAuthTokenDTO(null, null, ResultCode.AUTH_FAIL);
		}
	}

	@Override
	public AuthTokenDTO register(UserRegisterDTO dto)
	{
		if (dto == null || dto.getPassword() == null || dto.getUserPhone() == null)
		{
			return DTOFactory.createAuthTokenDTO(null, null, ResultCode.NULL_PARAM);
		}

		String phone = dto.getUserPhone();
		if (!PHONE_PATTERN.matcher(phone).matches())
		{
			LOGGER.info("非法的手机号:{}", phone);
			return DTOFactory.createAuthTokenDTO(null, null, ResultCode.INVALID_PARAM);
		}

		String password = dto.getPassword();
		if (password.length() > PASS_WORD_LENGTH_MAX || password.length() < PASS_WORD_LENGTH_MIN)
		{
			LOGGER.info("注册密码长度不符合要求:{}", password);
			return DTOFactory.createAuthTokenDTO(null, null, ResultCode.INVALID_PARAM);
		}

		if (!PASS_WORD_PATTERN.matcher(password).matches())
		{
			LOGGER.info("注册密码含有非法字符:{}", password);
			return DTOFactory.createAuthTokenDTO(null, null, ResultCode.INVALID_PARAM);
		}

		try
		{
			if (userInfoMapper.existUserByPhone(phone))
			{
				LOGGER.info("手机:{} 已被注册", phone);
				return DTOFactory.createAuthTokenDTO(null, null, ResultCode.DUP_USER);
			}
			String passwordMD5 = MD5Util.MD5(password + PASSWORD_SALT);
			UserInfoPO userInfo = POFactory.createUserInfoPO(phone, passwordMD5);
			userInfoMapper.insertOneUser(userInfo);
			String token = doAuth(userInfo.getId());
			LOGGER.info("手机:{}, 密码:{} 注册成功 token:{}", phone, password, token);
			return DTOFactory.createAuthTokenDTO(token, userInfo.getId(), ResultCode.GENERAL_SUCCESS);
		}
		catch (BusinessException e)
		{
			LOGGER.info(e.getResultCode().getDesc(), e);
			return DTOFactory.createAuthTokenDTO(null, null, e.getResultCode());
		}
		catch (Exception e)
		{
			LOGGER.info("用户注册时发生未知错误", e);
			return DTOFactory.createAuthTokenDTO(null, null, ResultCode.GENERAL_FAIL);
		}
	}
}
